package org.fujene.communicate.indexer.content;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Comparator;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.locks.ReentrantLock;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.fujene.constants.Constants;
import org.fujene.logging.LogParser;
import org.fujene.memman.MemoryManager;
import org.fujene.structure.LookupPointer;
import org.fujene.toolbox.ByteConverter;

public class ContentIndexerClient implements Runnable{
	private static final int STRLEN = Constants.BYTE_STRLEN;
	private static MemoryManager memman;
	private static PriorityBlockingQueue<ContentIndexerPackage> clientQueue;
	private static Object clientBlockade;
	private static GZIPOutputStream clientLog;
	private static File logFile;
	private static ReentrantLock indexLock;

	static{
		clientQueue =
			new PriorityBlockingQueue<ContentIndexerPackage>(10, new Comparator<ContentIndexerPackage>(){
				@Override
				public int compare(ContentIndexerPackage o1, ContentIndexerPackage o2){
					if(o1 == null || o2 == null) return 0;
					if(o1.ID() > o2.ID()) return 1;
					if(o1.ID() < o2.ID()) return -1;
					return 0;
				}
			});
		clientBlockade = new Object();
	}

	public ContentIndexerClient(boolean isCreateLog){
		if(isCreateLog && logFile == null){
			logFile = new File(".content_clientlog");
			if(!logFile.exists()) try{
				logFile.createNewFile();
			}catch(IOException e){
				System.err.println("Client-log file does not create. Indexing works without logging.");
			}
			try{
				clientLog = new GZIPOutputStream(new FileOutputStream(logFile));
			}catch(FileNotFoundException e){}catch(IOException e){}
		}
	}

	public static void property(MemoryManager manager){
		memman = manager;
		indexLock = new ReentrantLock();
	}

	private static void addLog(ContentIndexerPackage queue) throws IOException{
		byte[] strbytes = queue.getByteArray();
		clientLog.write(ByteConverter.intToByte(strbytes.length, STRLEN));
		clientLog.write(strbytes);
	}

	// Clear log file and re-construct
	public static void clearLog() throws IOException{
		if(clientLog != null){
			clientLog.close();
			logFile.delete();
			logFile.createNewFile();
			clientLog = new GZIPOutputStream(new FileOutputStream(logFile));
		}
	}

	// Revert from log file
	public static void revert() throws FileNotFoundException, IOException{
		GZIPInputStream logstream = null;
		try{
			logstream = new GZIPInputStream(new FileInputStream(logFile));
			byte[] strlenb = new byte[STRLEN];
			byte[] strb;
			while(logstream.available() != 0){
				logstream.read(strlenb);
				strb = new byte[ByteConverter.byteToInt(strlenb)];
				logstream.read(strb);

				clientQueue.add(ContentIndexerPackage.fromByteArray(strb));
			}
		}finally{
			if(logstream != null) logstream.close();
		}
	}

	public static void addToQueue(ContentIndexerPackage newIndex){
		// FIXME: Add log
		if(clientLog != null) try{
			addLog(newIndex);
		}catch(IOException e){
			e.printStackTrace();
		}
		clientQueue.offer(newIndex);
	}

	public static boolean isEmpty(){
		return clientQueue.isEmpty();
	}

	public static void waitUntilEmpty(){
		if(!clientQueue.isEmpty()) try{
			clientBlockade.wait();
		}catch(InterruptedException e){}
	}

	public static int residue(){
		return clientQueue.size();
	}

	@Override
	public void run(){
		ContentIndexerPackage newIndex = null;
		while(true){
			// Take from client queue
			try{
				newIndex = clientQueue.take();
				if(clientQueue.isEmpty()) synchronized(clientBlockade){
					clientBlockade.notifyAll();
				}
			}catch(InterruptedException e1){
				continue;
			}
			try{
				indexLock.lock();
//				System.out.println("[DEBUG]queue:  " + newIndex.ID() + " is stored");
				long startTime = LogParser.initTime();
				LookupPointer[] appPtr = new LookupPointer[newIndex.appendixSize()];
				for(int i = 0; i < newIndex.appendixSize(); i++)
					appPtr[i] =
						memman.storeAppendix(newIndex.appendix(i), newIndex.ID(), i, newIndex.cluster());
				startTime = LogParser.timerFinest("Index / Index appendix", startTime);
				for(int i = 0; i < newIndex.contentSize(); i++)
					memman.storeContent(newIndex.content(i), newIndex.ID(), i, appPtr, newIndex.cluster());
				startTime = LogParser.timerFinest("Index / Index content", startTime);
			}finally{
				indexLock.unlock();
			}
		}
	}

	@Override
	protected void finalize() throws Throwable{
		if(clientLog != null) clientLog.close();
		super.finalize();
	}
}
